package setup

import (
	"encoding/json"
	"fmt"
	"os"
	"path/filepath"
)

// InstallClaude installs Claude Code hooks
func InstallClaude(project bool) {
	var settingsPath string

	if project {
		settingsPath = ".claude/settings.local.json"
		fmt.Println("Installing Claude hooks for this project...")
	} else {
		home, err := os.UserHomeDir()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: failed to get home directory: %v\n", err)
			os.Exit(1)
		}
		settingsPath = filepath.Join(home, ".claude/settings.json")
		fmt.Println("Installing Claude hooks globally...")
	}

	// Ensure parent directory exists
	if err := EnsureDir(filepath.Dir(settingsPath), 0755); err != nil {
		fmt.Fprintf(os.Stderr, "Error: %v\n", err)
		os.Exit(1)
	}

	// Load or create settings
	var settings map[string]interface{}
	data, err := os.ReadFile(settingsPath) // #nosec G304 -- settingsPath is constructed from user home dir, not user input
	if err != nil {
		settings = make(map[string]interface{})
	} else {
		if err := json.Unmarshal(data, &settings); err != nil {
			fmt.Fprintf(os.Stderr, "Error: failed to parse settings.json: %v\n", err)
			os.Exit(1)
		}
	}

	// Get or create hooks section
	hooks, ok := settings["hooks"].(map[string]interface{})
	if !ok {
		hooks = make(map[string]interface{})
		settings["hooks"] = hooks
	}

	// Add SessionStart hook
	if addHookCommand(hooks, "SessionStart", "bd prime") {
		fmt.Println("✓ Registered SessionStart hook")
	}

	// Add PreCompact hook
	if addHookCommand(hooks, "PreCompact", "bd prime") {
		fmt.Println("✓ Registered PreCompact hook")
	}

	// Write back to file
	data, err = json.MarshalIndent(settings, "", "  ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: marshal settings: %v\n", err)
		os.Exit(1)
	}

	if err := atomicWriteFile(settingsPath, data, 0644); err != nil {
		fmt.Fprintf(os.Stderr, "Error: write settings: %v\n", err)
		os.Exit(1)
	}

	fmt.Printf("\n✓ Claude Code integration installed\n")
	fmt.Printf("  Settings: %s\n", settingsPath)
	fmt.Println("\nRestart Claude Code for changes to take effect.")
}

// CheckClaude checks if Claude integration is installed
func CheckClaude() {
	home, err := os.UserHomeDir()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: failed to get home directory: %v\n", err)
		os.Exit(1)
	}

	globalSettings := filepath.Join(home, ".claude/settings.json")
	projectSettings := ".claude/settings.local.json"

	globalHooks := hasBeadsHooks(globalSettings)
	projectHooks := hasBeadsHooks(projectSettings)

	if globalHooks {
		fmt.Println("✓ Global hooks installed:", globalSettings)
	} else if projectHooks {
		fmt.Println("✓ Project hooks installed:", projectSettings)
	} else {
		fmt.Println("✗ No hooks installed")
		fmt.Println("  Run: bd setup claude")
		os.Exit(1)
	}
}

// RemoveClaude removes Claude Code hooks
func RemoveClaude(project bool) {
	var settingsPath string

	if project {
		settingsPath = ".claude/settings.local.json"
		fmt.Println("Removing Claude hooks from project...")
	} else {
		home, err := os.UserHomeDir()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: failed to get home directory: %v\n", err)
			os.Exit(1)
		}
		settingsPath = filepath.Join(home, ".claude/settings.json")
		fmt.Println("Removing Claude hooks globally...")
	}

	// Load settings
	data, err := os.ReadFile(settingsPath) // #nosec G304 -- settingsPath is constructed from user home dir, not user input
	if err != nil {
		fmt.Println("No settings file found")
		return
	}

	var settings map[string]interface{}
	if err := json.Unmarshal(data, &settings); err != nil {
		fmt.Fprintf(os.Stderr, "Error: failed to parse settings.json: %v\n", err)
		os.Exit(1)
	}

	hooks, ok := settings["hooks"].(map[string]interface{})
	if !ok {
		fmt.Println("No hooks found")
		return
	}

	// Remove bd prime hooks
	removeHookCommand(hooks, "SessionStart", "bd prime")
	removeHookCommand(hooks, "PreCompact", "bd prime")

	// Write back
	data, err = json.MarshalIndent(settings, "", "  ")
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: marshal settings: %v\n", err)
		os.Exit(1)
	}

	if err := atomicWriteFile(settingsPath, data, 0644); err != nil {
		fmt.Fprintf(os.Stderr, "Error: write settings: %v\n", err)
		os.Exit(1)
	}

	fmt.Println("✓ Claude hooks removed")
}

// addHookCommand adds a hook command to an event if not already present
// Returns true if hook was added, false if already exists
func addHookCommand(hooks map[string]interface{}, event, command string) bool {
	// Get or create event array
	eventHooks, ok := hooks[event].([]interface{})
	if !ok {
		eventHooks = []interface{}{}
	}

	// Check if bd hook already registered
	for _, hook := range eventHooks {
		hookMap, ok := hook.(map[string]interface{})
		if !ok {
			continue
		}
		commands, ok := hookMap["hooks"].([]interface{})
		if !ok {
			continue
		}
		for _, cmd := range commands {
			cmdMap, ok := cmd.(map[string]interface{})
			if !ok {
				continue
			}
			if cmdMap["command"] == command {
				fmt.Printf("✓ Hook already registered: %s\n", event)
				return false
			}
		}
	}

	// Add bd hook to array
	newHook := map[string]interface{}{
		"matcher": "",
		"hooks": []interface{}{
			map[string]interface{}{
				"type":    "command",
				"command": command,
			},
		},
	}

	eventHooks = append(eventHooks, newHook)
	hooks[event] = eventHooks
	return true
}

// removeHookCommand removes a hook command from an event
func removeHookCommand(hooks map[string]interface{}, event, command string) {
	eventHooks, ok := hooks[event].([]interface{})
	if !ok {
		return
	}

	// Filter out bd prime hooks
	var filtered []interface{}
	for _, hook := range eventHooks {
		hookMap, ok := hook.(map[string]interface{})
		if !ok {
			filtered = append(filtered, hook)
			continue
		}

		commands, ok := hookMap["hooks"].([]interface{})
		if !ok {
			filtered = append(filtered, hook)
			continue
		}

		keepHook := true
		for _, cmd := range commands {
			cmdMap, ok := cmd.(map[string]interface{})
			if !ok {
				continue
			}
			if cmdMap["command"] == command {
				keepHook = false
				fmt.Printf("✓ Removed %s hook\n", event)
				break
			}
		}

		if keepHook {
			filtered = append(filtered, hook)
		}
	}

	hooks[event] = filtered
}

// hasBeadsHooks checks if a settings file has bd prime hooks
func hasBeadsHooks(settingsPath string) bool {
	data, err := os.ReadFile(settingsPath) // #nosec G304 -- settingsPath is constructed from known safe locations (user home/.claude), not user input
	if err != nil {
		return false
	}

	var settings map[string]interface{}
	if err := json.Unmarshal(data, &settings); err != nil {
		return false
	}

	hooks, ok := settings["hooks"].(map[string]interface{})
	if !ok {
		return false
	}

	// Check SessionStart and PreCompact for "bd prime"
	for _, event := range []string{"SessionStart", "PreCompact"} {
		eventHooks, ok := hooks[event].([]interface{})
		if !ok {
			continue
		}

		for _, hook := range eventHooks {
			hookMap, ok := hook.(map[string]interface{})
			if !ok {
				continue
			}
			commands, ok := hookMap["hooks"].([]interface{})
			if !ok {
				continue
			}
			for _, cmd := range commands {
				cmdMap, ok := cmd.(map[string]interface{})
				if !ok {
					continue
				}
				if cmdMap["command"] == "bd prime" {
					return true
				}
			}
		}
	}

	return false
}
